package main

import (
	"encoding/json"
	"flag"
	"fmt"
	"io/ioutil"
	"log"
	"net/http"
	"sync"
	"text/template"

	"github.com/gorilla/websocket"
)

var (
	addr     = flag.String("addr", ":8080", "http service address")
	upgrader = websocket.Upgrader{
		CheckOrigin: func(r *http.Request) bool { return true },
	}
	indexTemplate = &template.Template{}

	// lock for peerConnections and trackLocals
	listLock sync.RWMutex
	//peerConnections []peerConnectionState
	//trackLocals     map[string]*webrtc.TrackLocalStaticRTP
	connections []*websocket.Conn
	ws          threadSafeWriter
)

type websocketMessage struct {
	Event string `json:"event"`
	Data  string `json:"data"`
}

// type peerConnectionState struct {
// 	peerConnection *webrtc.PeerConnection
// 	websocket      *threadSafeWriter
// }

func main() {
	// Parse the flags passed to program
	flag.Parse()

	// Init other state
	//log.SetFlags(0)
	log.SetFlags(log.Lshortfile | log.LstdFlags)
	//trackLocals = map[string]*webrtc.TrackLocalStaticRTP{}

	// Read index.html from disk into memory, serve whenever anyone requests /
	indexHTML, err := ioutil.ReadFile("index.html")
	if err != nil {
		panic(err)
	}
	indexTemplate = template.Must(template.New("").Parse(string(indexHTML)))

	// websocket handler
	http.HandleFunc("/websocket", websocketHandler)

	// index.html handler
	http.HandleFunc("/", func(w http.ResponseWriter, r *http.Request) {
		if err := indexTemplate.Execute(w, "ws://"+r.Host+"/websocket"); err != nil {
			log.Fatal(err)
		}
	})

	// request a keyframe every 3 seconds
	// go func() {
	// 	for range time.NewTicker(time.Second * 3).C {
	// 		dispatchKeyFrame()
	// 		log.Println("dispatchKeyFrame() every 3 seconds")
	// 	}
	// }()

	// start HTTP server
	log.Fatal(http.ListenAndServe(*addr, nil))
}

// Add to list of tracks and fire renegotation for all PeerConnections
// func addTrack(t *webrtc.TrackRemote) *webrtc.TrackLocalStaticRTP {
// 	listLock.Lock()
// 	defer func() {
// 		listLock.Unlock()
// 		signalPeerConnections()
// 	}()

// 	// Create a new TrackLocal with the same codec as our incoming
// 	trackLocal, err := webrtc.NewTrackLocalStaticRTP(t.Codec().RTPCodecCapability, t.ID(), t.StreamID())
// 	if err != nil {
// 		panic(err)
// 	}
// 	log.Println("addTrack(),Create a new TrackLocal with the same codec as our incoming")
// 	trackLocals[t.ID()] = trackLocal
// 	return trackLocal
// }

// Remove from list of tracks and fire renegotation for all PeerConnections
// func removeTrack(t *webrtc.TrackLocalStaticRTP) {
// 	listLock.Lock()
// 	defer func() {
// 		listLock.Unlock()
// 		signalPeerConnections()
// 	}()
// 	log.Println("removeTrack")
// 	delete(trackLocals, t.ID())
// }

// signalPeerConnections updates each PeerConnection so that it is getting all the expected media tracks
// func signalPeerConnections() {
// 	listLock.Lock()
// 	defer func() {
// 		listLock.Unlock()
// 		dispatchKeyFrame()
// 	}()

// 	attemptSync := func() (tryAgain bool) {
// 		for i := range peerConnections {
// 			if peerConnections[i].peerConnection.ConnectionState() == webrtc.PeerConnectionStateClosed {
// 				peerConnections = append(peerConnections[:i], peerConnections[i+1:]...)
// 				return true // We modified the slice, start from the beginning
// 			}
// 			log.Println("peerConnections i:", i)
// 			// map of sender we already are seanding, so we don't double send
// 			existingSenders := map[string]bool{}

// 			for _, sender := range peerConnections[i].peerConnection.GetSenders() {
// 				if sender.Track() == nil {
// 					continue
// 				}

// 				existingSenders[sender.Track().ID()] = true
// 				log.Println("sender.Track().ID():", sender.Track().ID())
// 				// If we have a RTPSender that doesn't map to a existing track remove and signal
// 				if _, ok := trackLocals[sender.Track().ID()]; !ok {
// 					if err := peerConnections[i].peerConnection.RemoveTrack(sender); err != nil {
// 						return true
// 					}
// 				}
// 			}

// 			// Don't receive videos we are sending, make sure we don't have loopback
// 			for _, receiver := range peerConnections[i].peerConnection.GetReceivers() {
// 				if receiver.Track() == nil {
// 					continue
// 				}

// 				existingSenders[receiver.Track().ID()] = true
// 				log.Println("receiver.Track().ID():", receiver.Track().ID())
// 			}

// 			// Add all track we aren't sending yet to the PeerConnection
// 			for trackID := range trackLocals {
// 				if _, ok := existingSenders[trackID]; !ok {
// 					if _, err := peerConnections[i].peerConnection.AddTrack(trackLocals[trackID]); err != nil {
// 						return true
// 					}
// 				}
// 			}

// 			offer, err := peerConnections[i].peerConnection.CreateOffer(nil)
// 			if err != nil {
// 				return true
// 			}
// 			log.Println("offer:", offer)
// 			if err = peerConnections[i].peerConnection.SetLocalDescription(offer); err != nil {
// 				return true
// 			}

// 			offerString, err := json.Marshal(offer)
// 			if err != nil {
// 				return true
// 			}
// 			//log.Println("offerString:", offerString)
// 			if err = peerConnections[i].websocket.WriteJSON(&websocketMessage{
// 				Event: "offer",
// 				Data:  string(offerString),
// 			}); err != nil {
// 				return true
// 			}
// 		}

// 		return
// 	}

// 	for syncAttempt := 0; ; syncAttempt++ {
// 		if syncAttempt == 25 {
// 			// Release the lock and attempt a sync in 3 seconds. We might be blocking a RemoveTrack or AddTrack
// 			go func() {
// 				time.Sleep(time.Second * 3)
// 				signalPeerConnections()
// 			}()
// 			return
// 		}

// 		if !attemptSync() {
// 			break
// 		}
// 	}
// }

// dispatchKeyFrame sends a keyframe to all PeerConnections, used everytime a new user joins the call
// func dispatchKeyFrame() {
// 	listLock.Lock()
// 	defer listLock.Unlock()

// 	for i := range peerConnections {
// 		for _, receiver := range peerConnections[i].peerConnection.GetReceivers() {
// 			if receiver.Track() == nil {
// 				continue
// 			}

// 			_ = peerConnections[i].peerConnection.WriteRTCP([]rtcp.Packet{
// 				&rtcp.PictureLossIndication{
// 					MediaSSRC: uint32(receiver.Track().SSRC()),
// 				},
// 			})
// 		}
// 	}
// }
func removeConn(connToRemove *websocket.Conn) {
	if len(connections) <= 0 {
		return
	}
	for i, con := range connections {
		if connToRemove == con {
			connections = append(connections[:i], connections[i+1:]...)
		}
	}
}

// Handle incoming websockets
func websocketHandler(w http.ResponseWriter, r *http.Request) {
	// Upgrade HTTP request to Websocket
	//使用connections数组，可切片，客户端断开了要删除
	unsafeConn, err := upgrader.Upgrade(w, r, nil)

	if err != nil {
		log.Print("upgrade:", err)
		return
	}
	//fmt.Printf("unsafeConn:%v", unsafeConn)
	//log.Println("websocketHandler,unsafeConn:", unsafeConn)
	c := &threadSafeWriter{unsafeConn, sync.Mutex{}}
	fmt.Printf("c:%v,%T", c, c)

	// When this frame returns close the Websocket
	defer c.Close() //nolint

	if len(connections) > 0 {
		for _, con := range connections {
			if writeErr := con.WriteJSON(&websocketMessage{
				Event: "anotherClientJoin",
				Data:  string("anotherClientJoin test"),
			}); writeErr != nil {
				log.Println(writeErr)
			}
		}
	}
	connections = append(connections, unsafeConn)

	// Create new PeerConnection
	// peerConnection, err := webrtc.NewPeerConnection(webrtc.Configuration{})
	// if err != nil {
	// 	log.Print(err)
	// 	return
	// }

	// When this frame returns close the PeerConnection
	// defer peerConnection.Close() //nolint

	// Accept one audio and one video track incoming
	// for _, typ := range []webrtc.RTPCodecType{webrtc.RTPCodecTypeVideo, webrtc.RTPCodecTypeAudio} {
	// 	log.Println("incoming typ:", typ)
	// 	if _, err := peerConnection.AddTransceiverFromKind(typ, webrtc.RTPTransceiverInit{
	// 		Direction: webrtc.RTPTransceiverDirectionRecvonly,
	// 	}); err != nil {
	// 		log.Print(err)
	// 		return
	// 	}
	// }

	// Add our new PeerConnection to global list
	// listLock.Lock()
	// peerConnections = append(peerConnections, peerConnectionState{peerConnection, c})
	// listLock.Unlock()

	// Trickle ICE. Emit server candidate to client
	// peerConnection.OnICECandidate(func(i *webrtc.ICECandidate) {
	// 	if i == nil {
	// 		return
	// 	}
	// 	log.Println("new incoming candidate:", i.ToJSON())
	// 	candidateString, err := json.Marshal(i.ToJSON())
	// 	if err != nil {
	// 		log.Println(err)
	// 		return
	// 	}

	// 	if writeErr := c.WriteJSON(&websocketMessage{
	// 		Event: "candidate",
	// 		Data:  string(candidateString),
	// 	}); writeErr != nil {
	// 		log.Println(writeErr)
	// 	}
	// })

	// If PeerConnection is closed remove it from global list
	// peerConnection.OnConnectionStateChange(func(p webrtc.PeerConnectionState) {
	// 	switch p {
	// 	case webrtc.PeerConnectionStateFailed:
	// 		if err := peerConnection.Close(); err != nil {
	// 			log.Print(err)
	// 		}
	// 	case webrtc.PeerConnectionStateClosed:
	// 		signalPeerConnections()
	// 	}
	// })

	// peerConnection.OnTrack(func(t *webrtc.TrackRemote, _ *webrtc.RTPReceiver) {
	// 	// Create a track to fan out our incoming video to all peers
	// 	trackLocal := addTrack(t)
	// 	defer removeTrack(trackLocal)
	// 	log.Println("OnTrack trackLocal:", trackLocal)
	// 	buf := make([]byte, 1500)
	// 	for {
	// 		i, _, err := t.Read(buf)
	// 		if err != nil {
	// 			return
	// 		}

	// 		if _, err = trackLocal.Write(buf[:i]); err != nil {
	// 			return
	// 		}
	// 	}
	// })

	// Signal for the new PeerConnection
	//signalPeerConnections()

	message := &websocketMessage{}
	for {
		_, raw, err := c.ReadMessage()
		if err != nil {
			//fmt.Printf("%v", err)
			log.Println(err)
			return
		} else if err := json.Unmarshal(raw, &message); err != nil {
			log.Println(err)
			return
		}

		if len(connections) > 0 {
			for _, con := range connections {
				//log.Println("con:", con)
				// if writeErr := con.WriteJSON(raw); writeErr != nil {
				// 	log.Println(writeErr)
				// }
				if con != unsafeConn {
					if writeErr := con.WriteJSON(&websocketMessage{
						Event: message.Event,
						Data:  string(message.Data),
						//Data: string("{\"candidate\":\"candidate:375476284 1 udp 2130706431 192.168.1.199 36156 typ host\",\"sdpMid\":\"\",\"sdpMLineIndex\":0,\"usernameFragment\":null}"),
					}); writeErr != nil {
						log.Println(writeErr)
					}
				}
			}
		}

		//log.Println("message:", message)
		switch message.Event {

		case "candidate":
			// candidate := webrtc.ICECandidateInit{}
			// if err := json.Unmarshal([]byte(message.Data), &candidate); err != nil {
			// 	log.Println(err)
			// 	return
			// }

			// if err := peerConnection.AddICECandidate(candidate); err != nil {
			// 	log.Println(err)
			// 	return
			// }
			// if len(connections) > 0 {
			// 	for _, con := range connections {
			// 		if writeErr := con.WriteJSON(&websocketMessage{
			// 			Event: "anotherClientJoin",
			// 			Data:  string("anotherClientJoin test"),
			// 		}); writeErr != nil {
			// 			log.Println(writeErr)
			// 		}
			// 	}
			// }
			log.Println("candidate:", message)
			log.Println("len:", len(connections))

			// if len(connections) > 0 {
			// 	for _, con := range connections {
			// 		//log.Println("con:", con)
			// 		// if writeErr := con.WriteJSON(raw); writeErr != nil {
			// 		// 	log.Println(writeErr)
			// 		// }
			// 		// if con != unsafeConn {
			// 		// 	if writeErr := con.WriteJSON(&websocketMessage{
			// 		// 		Event: "candidate",
			// 		// 		Data:  string(message.Data),
			// 		// 	}); writeErr != nil {
			// 		// 		log.Println(writeErr)
			// 		// 	}
			// 		// }
			// 	}
			// }
			break
		case "answer":
			// answer := webrtc.SessionDescription{}
			// if err := json.Unmarshal([]byte(message.Data), &answer); err != nil {
			// 	log.Println(err)
			// 	return
			// }

			// if err := peerConnection.SetRemoteDescription(answer); err != nil {
			// 	log.Println(err)
			// 	return
			// }
		}
	}
}

// Helper to make Gorilla Websockets threadsafe
type threadSafeWriter struct {
	*websocket.Conn
	sync.Mutex
}

func (t *threadSafeWriter) WriteJSON(v interface{}) error {
	t.Lock()
	defer t.Unlock()

	return t.Conn.WriteJSON(v)
}
